<?php

namespace app\common\exception;

use app\api\controller\Base;
use app\common\model\SystemException;
use think\exception\Handle;
use think\exception\HttpException;
use think\exception\ValidateException;
use think\facade\Db;
use think\facade\Log;
use think\Request;
use think\Response;
use Throwable;

class ExceptionHandle extends Handle
{

    /**
     * 请求信息
     * @var Request
     */
    protected $request;

    public function render($request, Throwable $e): Response
    {
        $type1 = $e instanceof BusinessException;
        $type2 = $e instanceof ValidateException;
        $type3 = $e instanceof HttpException;
        if (!$type1 && !$type2 && !$type3) {
            $this->save($e);
        }
        return app(Base::class)->response($this->format($e), 0, $e->getMessage());
    }

    /**
     * format
     * @param $e
     * @return array
     */
    protected function format($e): array
    {
        $data = [
            'message' => $e->getMessage(),
            'file' => $e->getFile(),
            'line' => $e->getLine(),
            'trace' => $e->getTrace(),
        ];
        if (!$this->app->isDebug()) {
            unset($data['trace']);
        }
        return $data;
    }

    /**
     * save
     * @param $e
     * @return void
     */
    protected function save($e)
    {
        // 组装异常
        $exceptionData = [
            'req' => json_encode($this->getReqInfo(), JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT),
            'con' => json_encode([
                'message' => $e->getMessage(),
                'file' => $e->getFile(),
                'line' => $e->getLine(),
                'trace' => $e->getTrace(),
            ], JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT)
        ];

        // 保存日志
        Log::record($exceptionData, 'error');

        // 保存数据库
        Db::rollback(); // 临时解决未手动释放事务的问题,后期要遵守规范(紧张学生乱提交事务不会滚)
        Db::transaction(function () use ($exceptionData) {
            $model = new SystemException();
            $model->save($exceptionData);
        });
    }

    /**
     * 获取路由信息
     * @return array
     */
    protected function getReqInfo(): array
    {
        $request = app(Request::class);
        return [
            'ip' => $request->ip(),
            'method' => $request->method(),
            'host' => $request->host(),
            'uri' => $request->url(),
            'rule' => $request->rule()->getRule(),
            'route' => $request->rule()->getRoute(),
            'option' => $request->rule()->getOption(),
            'var' => $request->rule()->getVars(),
            'param' => $request->param()
        ];
    }
}